home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
viewers
/
polyview
/
polyvw31.lha
/
Polyview3.1
/
new
/
pvpick.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-13
|
22KB
|
776 lines
/*****************************************************************************
* NCSA Polyview 3.0 *
* *
* Version 3 changes and additions by Marc Andreessen. *
* Version 2 by Brian Calvert. *
* *
* Software Development Group *
* National Center for Supercomputing Applications *
* University of Illinois at Urbana-Champaign *
* *
* This is BETA release software. As such it may contain software bugs and *
* exhibit inconsistencies. *
* *
* Please send bug reports to polyview@ncsa.uiuc.edu. *
* *
* Copyright (c) 1992 The Board of Trustees of the University of Illinois. *
* *
* Permission to use, copy, and modify this software and its *
* documentation for educational, research, and non-profit purposes is *
* hereby granted, provided that the above copyright notice, the original *
* authors names, and this permission notice appear in all such copies. *
* Any distribution of this software requires the explicit and written *
* authorization of the authors. *
* *
* The University of Illinois makes no representations about the *
* suitability of this software for any purpose. It is provided "as is" *
* without warranty of any kind. *
*****************************************************************************/
/* $Header: /usr3/people/gbourhis/pv3/new/RCS/pvpick.c,v 1.2 93/07/13 16:45:06 gbourhis Exp $ */
#ifdef RCSLOG
$Log: pvpick.c,v $
* Revision 1.2 93/07/13 16:45:06 gbourhis
* fix a bug in PickVertices(): loadname() must not be called between
* bgnpoint() & endpoint().
*
* Revision 1.1 92/09/18 10:55:26 marca
* Initial revision
*
#endif
#include "pv.h"
#define PI 3.14159265
/* ------------------------------ PickRegion ------------------------------ */
int PickRegion (state_t *state, window_t *win, short left, short bottom,
short right, short top, int toggle, int interactive)
{
polyview_t *pv = (polyview_t *)WIN_IMAGE(win);
char line[MAXLINELEN];
XmxWinset (pv->drawing_volume);
/* If the pick window has zero area in either dimension, grow it */
/* equally in both directions. */
if (left == right)
{
left--;
right++;
}
if (bottom == top)
{
bottom--;
top++;
}
/* Echo the picking region to the transcript file. */
if (state->transcript_fp != NULL)
fprintf(state->transcript_fp, "select area %d %d %d %d %s\n",
(int) left, (int) bottom, (int) right, (int) top,
(toggle) ? "toggle" : "notoggle");
if (!interactive)
{
sprintf (line, "select area %d %d %d %d %s\0",
(int) left, (int) bottom, (int) right, (int) top,
(toggle) ? "toggle" : "notoggle");
GUIaddCommand (line);
}
/* Recursively select from the objects. */
PickPolyview(state, win, win->pickmode, left, right, bottom, top,
toggle);
/* Send the message that something new was picked.
This effects a set_redraw call. */
broadcast_msg(state, win, MSG_NEWPICK);
return ST_OKAY;
}
/* ---------------------------- PickNoObjects ----------------------------- */
static long PickNoObjects (state_t *state, object_t *object, va_list args)
{
OBJ_PICKMODE(object) = PICK_NONE;
OBJ_HITS(object) = 0;
return 1;
}
/* -------------------------------- PickNo -------------------------------- */
void PickNo (state_t *state, window_t *win)
{
/* Unselect all of the objects in the tree. */
if (WinHasData(win) && (WIN_ROOTOBJ(win) != NULL))
ApplyFuncToTree(state, WIN_ROOTOBJ(win), PickNoObjects);
return;
}
/* ---------------------------- PickAllObjects ---------------------------- */
static long PickAllObjects (state_t *state, object_t *object, va_list args)
{
OBJ_PICKMODE(object) = PICK_ALL;
OBJ_HITS(object) = 1;
return 1;
}
/* ------------------------------- PickAll -------------------------------- */
void PickAll (state_t *state, window_t *win)
{
/* Select all of the objects in the tree. */
if (WinHasData(win) && (WIN_ROOTOBJ(win) != NULL))
ApplyFuncToTree(state, WIN_ROOTOBJ(win), PickAllObjects);
return;
}
/* ----------------------------- PickPolyview ----------------------------- */
int PickPolyview (state_t *state, window_t *win, pickmode_t mode,
long left, long right, long bottom, long top, int toggle)
{
polyview_t *polyview;
object_t *obj;
float maxwidth;
static Matrix ident =
{
{1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}
}, s;
int i, j;
float xaspect, yaspect;
float width, height;
float bx, by;
float dx, dy;
float tx, ty;
Matrix op;
long first_id;
/* Get a pointer to the root object associated with the window. */
polyview = (polyview_t *) win->image;
assert (polyview != NULL);
if (WIN_FRAME(win) == NULL)
{
/* Make sure there's really nothing to draw. */
assert (WIN_FRAMELIST(win) == NULL);
/* There's nothing to draw. */
return ST_OKAY;
}
else
{
obj = FRA_ROOTOBJ(WIN_FRAME(win));
}
/* Make sure that the object exists, has information associated */
/* with it, and has a drawing function. */
assert (obj != NULL);
assert (obj->info != NULL);
assert (obj->info->draw_fn != NULL);
/* Save the transformation matrix and the drawing attributes. */
mmode (MPROJECTION);
getmatrix (s);
mmode (MVIEWING);
/* When MSINGLE mode is entered, matrix and stack are cleared out
anyway, so no reason to save them. */
/* pushmatrix(); */
pushviewport();
pushattributes();
mmode (MSINGLE);
/* Set up the window's drawing conditions: projection matrix, */
/* zbuffering, shading, and so on. */
maxwidth = WIN_MAXWIDTH(win);
height = (float) WIN_HEIGHT(win);
width = (float) WIN_WIDTH(win);
if (WIN_PROJECTION(win) == ORTHOGRAPHIC)
{
/* Use the spherical viewing distance to determine the scale */
/* to magnify by. */
float scl;
/* Calculate the aspect ratio acaling multiplier. This is */
/* applied to the x-dimension to adjust for the change in */
/* aspect ratio of a resizable window. */
if (width > height)
{
xaspect = height / width;
yaspect = 1.0;
}
else
{
xaspect = 1.0;
yaspect = width / height;
}
/* Initialize the transformation matrix */
mmode (MPROJECTION);
loadmatrix(ident);
/* Build an orthographic projection matrix and load it. */
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
op[i][j] = 0.0;
op[0][0] = (2 / maxwidth) * xaspect;
op[1][1] = 2 / maxwidth * yaspect;
op[2][2] = -2 / maxwidth;
op[3][3] = 1.0;
multmatrix(op);
mmode (MVIEWING);
loadmatrix (ident);
scl = maxwidth/(WIN_SFROM(win)[RHO]);
scale(scl, scl, 1.0);
}
else
{
float dist;
dist = fhypot(WIN_CFROM(win)[X]-WIN_STATS(win,PX).mean,
fhypot(WIN_CFROM(win)[Y]-
WIN_STATS(win,PY).mean,
WIN_CFROM(win)[Z]-
WIN_STATS(win,PZ).mean));
perspective(WIN_FOV(win),
(float) (WIN_WIDTH(win))/WIN_HEIGHT(win),
MAX(0.01, dist-1.1*maxwidth), dist+1.1*maxwidth);
translate(0.0, 0.0, -WIN_SFROM(win)[RHO]);
}
/* Insert the region-drawing transformation before the current viewing */
/* transformation. */
getmatrix(op);
loadmatrix(ident);
bx = left;
by = bottom;
dx = right - left;
dy = top - bottom;
scale(width/dx, height/dy, 1.0);
tx = -2.0 * (((bx + dx/2.0) - width/2.0)/width);
ty = -2.0 * (((by + dy/2.0) - height/2.0)/height);
translate(tx, ty, 0.0);
multmatrix(op);
/* This rotate should match the rotate in pvpoly.c */
rotate (WIN_TWIST(win), 'z');
rot(90.0-WIN_SFROM(win)[PHI]*180.0/PI,'x');
rot(-90.0-WIN_SFROM(win)[THETA]*180.0/PI,'y');
rot(-90.0, 'x');
translate(-WIN_AT(win)[X],-WIN_AT(win)[Y],-WIN_AT(win)[Z]);
zbuffer(FALSE);
/* Get the image root object and recursively pick the object */
/* structure. If nothing is selected, that effectively means that */
/* all objects are selected. */
first_id = 1;
if (PickObjRecurs(state, win, obj, obj->info, mode, toggle, &first_id) == 0)
PickAll(state, win);
/* Restore the transformation matrix and z-buffer. */
mmode (MPROJECTION);
loadmatrix (s);
mmode (MVIEWING);
popattributes();
popviewport();
/* When MSINGLE is entered/left, matrix stack is emptied. */
/* popmatrix(); */
zbuffer(TRUE);
return ST_OKAY;
}
/* ---------------------------- PickObjRecurs ----------------------------- */
int PickObjRecurs (state_t *state, window_t *win, object_t *obj,
objinfo_t *info, pickmode_t mode, int toggle,
long *first_id)
{
objinfo_t local_info;
object_t *child;
long hits;
/* If we don't need to pick this object tree, then return. */
if (obj->type & INACTIVE)
return 0;
/* Save the current transformation matrix context, since update_info */
/* may change it and the recursive drawing steps will want to use */
/* it. */
pushmatrix();
pushattributes();
/* Process the new attributes and transformations to develop a new */
/* local info block. Update only those fields that have new values. */
update_info(state, info, obj->info, &local_info);
/* Set the id of the object's first selected element, if any. */
OBJ_FIRSTID(obj) = *first_id;
/* Using this information, either draw the object or decompose it */
/* further. */
if (obj->type & ATOM)
{
/* Go ahead and draw objects using the currently active draw */
/* function */
switch (mode)
{
case PICK_VERTEX:
hits = PickVertices(state, win, obj, info, toggle);
break;
case PICK_EDGE:
hits = PickEdges(state, win, obj, info, toggle);
break;
case PICK_FACE:
hits = PickFaces(state, win, obj, info, toggle);
break;
case PICK_OBJECT:
hits = PickObject(state, win, obj, info, toggle);
break;
default:
stprintf(state, "SYSTEM ERROR: Illegal pickmode option.\n");
}
/* If no hits on this object, set its mode to PICK_NONE. */
if (hits == 0)
OBJ_PICKMODE(obj) = PICK_NONE;
/* Increment the count of hit elements. */
*first_id += hits;
}
else if (obj->type & OBJECT)
{
/* Recursively examine all the objects in the group. */
hits = 0;
for (child = obj->u.grp.children; child != NULL;
child = child->sibling)
hits += PickObjRecurs(state, win, child,
&local_info, mode, toggle, first_id);
OBJ_HITS(obj) = hits;
if (hits == 0)
OBJ_PICKMODE(obj) = PICK_NONE;
else
OBJ_PICKMODE(obj) = PICK_BELOW;
}
else
{
stprintf(state, "SYSTEM ERROR: Object type not defined.\n");
popattributes();
popmatrix();
return 0;
}
/* Restore the transformation matrix. */
popattributes();
popmatrix();
return hits;
}
/* --------------------------- CreatePickVdata ---------------------------- */
char *CreatePickVdata (state_t *state, object_t *obj)
{
int num;
/* Get the data. If it does not exist, create it. */
if (OBJ_INFO(obj) == NULL)
{
stprintf(state, "ERROR: Object does not have info record.\n");
return NULL;
}
if (OBJ_PICKLIST(obj) == NULL)
{
/* Make the vdata large enough to handle either vertices */
/* or faces. */
num = MAX(OBJ_STATS(obj, PCOORD, 0).rec_count,
OBJ_STATS(obj, MCONNECT, 0).rec_count);
OBJ_PICKLIST(obj) = PVCALLOC(num, char);
OBJ_PICKLISTSIZE(obj) = num;
}
return obj->info->picked;
}
/* -------------------------- ProcessSelections --------------------------- */
/* ProcessSelections gets the ids of "hit" objects and sets or clears the */
/* corresponding bytes in the PICKED dataset. Returns the number of */
/* objects that were hit. */
long ProcessSelections (short *buffer, long count, char *picked, int toggle)
{
long num, hits;
long i;
long base;
char *pickflag;
PVD (("Entered ProcessSelections, count is %d\n", count));
/* Close the selection buffer and find out how many hits there were. */
hits = 0;
num = endselect(buffer);
/* Clear the next few entries in the picked buffer. */
base = 256 * ((count-1)/256);
PVD (("base is %d\n", base));
if (toggle)
{
for (i = base; i < count; i++)
{
PVD (("base is %d count is %d i is %d\n", base, count, i));
if (picked[i] == TRUE)
hits++;
}
}
else
{
for (i = base; i < count; i++)
{
PVD (("base is %d count is %d i is %d\n", base, count, i));
picked[i] = FALSE;
}
}
/* Using count as a base in the PICKED dataset, hits in the buffer */
/* are copied to it. */
for (i = 0; i < num; i++)
{
pickflag = &picked[base + buffer[i*2+1]];
if (toggle)
{
if (*pickflag)
{
hits--;
*pickflag = FALSE;
}
else
{
hits++;
*pickflag = TRUE;
}
}
else
{
hits++;
*pickflag = TRUE;
}
}
return hits;
}
/* ----------------------------- PickVertices ----------------------------- */
long PickVertices (state_t *state, window_t *win, object_t *obj,
objinfo_t *info, int toggle)
{
long count;
Vdata_t **vdata;
Vdata_t *pcoord_v;
float (*pcoord)[DIMS];
char *picked;
short buffer[1024];
long hits;
/* Get pointers to the pertinent data sets */
vdata = obj->u.obj.vdata;
if ( (pcoord_v = get_vdata(state, vdata[PCOORD])) == NULL)
return 0;
/* Get a pointer to the PICKED dataset. Create it, if necessary. */
picked = CreatePickVdata(state, obj);
/* Set up the consolidated coordinate array pointer. */
pcoord = (float (*)[DIMS]) pcoord_v->data;
count = 0;
assert (pcoord_v->stats[0].type == PVFLOAT);
/* No color information. Use foreground and draw the */
/* appropriate shapes. */
initnames();
hits = 0;
gselect(buffer, 1024);
while (count < pcoord_v->stats[0].rec_count)
{
/* Draw the vertex. */
loadname((short) (count&255));
bgnpoint();
v3f(*pcoord);
endpoint();
/* Move to the next x, y, z set and decrement the */
/* count. */
pcoord++;
count++;
/* The Iris has a limit of 255 points before */
/* the buffer has to be flushed. */
if (!(count & 255))
{
hits += ProcessSelections(buffer, count, picked, toggle);
initnames();
gselect(buffer, 1024);
}
}
hits += ProcessSelections(buffer, count, picked, toggle);
/* Set the object's pick mode and hits accordingly. */
OBJ_HITS(obj) = hits;
if (OBJ_HITS(obj) == 0)
OBJ_PICKMODE(obj) = PICK_NONE;
else
OBJ_PICKMODE(obj) = PICK_VERTEX;
return hits;
}
/* ------------------------------ PickEdges ------------------------------- */
long PickEdges (state_t *state, window_t *win, object_t *obj, objinfo_t *info,
int toggle)
{
long count;
Vdata_t **vdata;
Vdata_t *pcoord_v;
Vdata_t *connect_v;
float (*pcoord)[DIMS];
int *connect;
char *picked;
int i;
int nvert;
int vertex;
short buffer[1024];
long hits;
/* Get pointers to the pertinent data sets */
vdata = obj->u.obj.vdata;
if ( (connect_v = get_vdata(state, vdata[CONNECT])) == NULL)
{
if ( (connect_v = get_vdata(state, vdata[MCONNECT])) == NULL)
return PickVertices(state, win, obj, info, toggle);
}
if ( (pcoord_v = get_vdata(state, vdata[PCOORD])) == NULL)
return 0;
/* Get a pointer to the PICKED vdata. Create it, if necessary. */
picked = CreatePickVdata(state, obj);
/* Collect connectivity information for drawing. */
connect = (int *) connect_v->data;
count = 0;
nvert = connect_v->stats[0].rec_size;
/* Set up the consolidated coordinate array pointer. */
pcoord = (float (*)[DIMS]) pcoord_v->data;
assert (pcoord_v->stats[0].type == PVFLOAT);
/* No color information. Use foreground and draw the */
/* appropriate shapes. */
initnames();
hits = 0;
gselect(buffer, 1024);
while (count < connect_v->stats[0].rec_count)
{
loadname((short) count&255);
bgnclosedline();
for (i = nvert; i > 0; i--)
{
if ( (vertex = *(connect++)-1) == -1)
{
connect += i-1;
break;
}
v3f(pcoord[vertex]);
}
endclosedline();
count++;
/* The Iris has a limit of 255 points before */
/* the buffer has to be flushed. */
if (!(count & 255))
{
hits += ProcessSelections(buffer, count, picked, toggle);
initnames();
gselect(buffer, 1024);
}
}
hits += ProcessSelections(buffer, count, picked, toggle);
/* Set the object's pick mode and hits accordingly. */
OBJ_HITS(obj) = hits;
if (OBJ_HITS(obj) == 0)
OBJ_PICKMODE(obj) = PICK_NONE;
else
OBJ_PICKMODE(obj) = PICK_EDGE;
return hits;
}
/* ------------------------------ PickFaces ------------------------------- */
long PickFaces (state_t *state, window_t *win, object_t *obj, objinfo_t *info,
int toggle)
{
long count;
Vdata_t **vdata;
Vdata_t *pcoord_v;
Vdata_t *connect_v;
float (*pcoord)[DIMS];
int *connect;
char *picked;
int i;
int nvert;
int vertex;
short buffer[1024];
long hits;
/* Get pointers to the pertinent data sets */
vdata = obj->u.obj.vdata;
if ( (connect_v = get_vdata(state, vdata[CONNECT])) == NULL)
if ( (connect_v = get_vdata(state, vdata[MCONNECT])) == NULL)
return PickVertices(state, win, obj, info, toggle);
if ( (pcoord_v = get_vdata(state, vdata[PCOORD])) == NULL)
return 0;
/* Get a pointer to the PICKED vdata. Create it, if necessary. */
picked = CreatePickVdata(state, obj);
/* Collect connectivity information for drawing. */
connect = (int *) connect_v->data;
count = 0;
nvert = connect_v->stats[0].rec_size;
/* Set up the consolidated coordinate array pointer. */
pcoord = (float (*)[DIMS]) pcoord_v->data;
assert (pcoord_v->stats[0].type == PVFLOAT);
/* No color information. Use foreground and draw the */
/* appropriate shapes. */
hits = 0;
initnames();
gselect(buffer, 1024);
PVD (("Starting; count is %d, connect_v->stats[0].rec_count is %d\n",
count, connect_v->stats[0].rec_count));
while (count < connect_v->stats[0].rec_count)
{
/* PVD (("Loading name %d\n", (short) (count&255))); */
loadname((short) (count&255));
bgnpolygon();
for (i = nvert; i > 0; i--)
{
if ( (vertex = *(connect++)-1) == -1)
{
connect += i-1;
break;
}
v3f(pcoord[vertex]);
}
endpolygon();
count++;
/* The Iris has a limit of 256 points before */
/* the buffer has to be flushed. */
if (!(count & 255))
{
PVD (("Satisfied !(count & 255), resetting select\n"));
PVD (("Passing ProcessSelections count=%d\n", count));
hits += ProcessSelections(buffer, count, picked, toggle);
initnames();
gselect(buffer, 1024);
}
}
hits += ProcessSelections(buffer, count, picked, toggle);
/* Set the object's pick mode and hits accordingly. */
OBJ_HITS(obj) = hits;
if (OBJ_HITS(obj) == 0)
OBJ_PICKMODE(obj) = PICK_NONE;
else
OBJ_PICKMODE(obj) = PICK_FACE;
return hits;
}
/* ------------------------------ PickObject ------------------------------ */
long PickObject (state_t *state, window_t *win, object_t *obj,
objinfo_t *info, int toggle)
{
long hit;
pickmode_t prev_pickmode;
prev_pickmode = OBJ_PICKMODE(obj);
hit = (PickFaces(state, win, obj, info, FALSE) != 0);
/* If the object was previously selected and the user wants to */
/* toggle objects that were hit, and the object was hit, then */
/* turn it off. */
if (toggle)
{
if (prev_pickmode == PICK_OBJECT)
{
if (hit)
hit = 0;
else
hit = 1;
}
}
if (hit)
{
OBJ_HITS(obj) = 1;
OBJ_PICKMODE(obj) = PICK_OBJECT;
}
else
{
OBJ_HITS(obj) = 0;
OBJ_PICKMODE(obj) = PICK_NONE;
}
return OBJ_HITS(obj);
}